Linux Kernel image

您所在的位置:网站首页 EmuELEC 编译内核 kernelimg Linux Kernel image

Linux Kernel image

2024-07-15 21:03| 来源: 网络整理| 查看: 265

学习Linux Kernel image in different forms

1.内核镜像介绍

  通常一个可启动的内核镜像 (bootable kernel image) 是经过算法压缩的,2.6.30 之后采用 LZMA 或者 BZIP2,vmlinuz 最后的 z 表示内核是压缩的,这也意味着内核中会有一段解压程序。

  内核中包含了各种内核镜像的格式,如 vmlinux、zImage、bzImage、uImage 等,首先介绍内核中常见内核文件。

vmlinux是最原始,静态链接的,可执行的,不能bootable的,未压缩的内核镜像。vm代表Virtual Memory。Linux支持虚拟内存,因此得名vm。它是通过源码经过编译汇编, 链接而成的 ELF 文件。

zImage是 vmlinux 经过 gzip 压缩后的文件,适用于小内核。

bzImage 是 vmlinux 经过 gzip 压缩后的文件,适用于大内核,”bz” 表示 “big zImage”。

uImage 是 uboot 专用的镜像文件,它是在 zImage 之前加上一个长度为 0x40 的头信息,包括了该镜像文件的类型、加载位置、生成时间、大小等信息。

2.内核镜像构建 在这里插入图片描述 2.1.文件描述

1.vmlinux   vmlinux是最原始,未压缩的内核镜像。vm代表Virtual Memory。Linux支持虚拟内存,因此得名vm。它是通过源码经过编译汇编, 链接而成的 ELF 文件。因此这个 vmlinux 文件包含了 ELF 的属性,以及各种调试信息等,因此这个阶段的内核镜像 vmlinux 特别大,而且不能直接在 arm 上直接运行。

2.Image   由于 vmlinux 镜像体积巨大而且不能在 arm 上运行,因此需要使用 objcopy工具将不需要 的 section 从 vmlinux 里面剥离出来,最终在 arch/arm64/boot/目录下生成 Image 文件, 此时 Image 是可以在 arm 平台上运行的,该镜像文件也是未压缩。由于历史原因,当年制作出 Image 的大小正好比一个软盘大一点,为了让内核镜像能够装在一张软盘上,所以就将 Image 进行压缩, 生成 piggy.gz 或者 piggy_data.

3.piggy.gz/piggy_data

  The file Image compressed with gzip.一开始只支持 gzip 压缩方法,所以将压缩之后的 Image 称为 piggy.gz,但随着内核的不断 发展,内核支持更多的压缩算法,因此把压缩之后的 Image 称为 piggy_data.

4.piggy.o

  之前说过 Image 可以在 arm 上运行,当不能直接运行,因为 Image 运行前需要一些已知 初始化环境,这就需要特定功能的代码实现这些功能,这里称这些代码为 bootstrap。 于是内核在 arch/arm/boot/compressed/ 目录下增加了 bootstrap 功能的代码。和制作 vmlinux 一样,需要将这个目录下的源文件编译汇编成目标文件,然后再链接成一个文件。 为了构造这个,内核将 piggy_data 直接塞到了一个汇编文件 piggy.S 中,然后这个文件 经过汇编之后,就生成了 piggy.o。

2.2.vmlinux 构建过程

  vmlinux 文件是 Kbuild 编译系统将源码经过编译链接所获得的目标文件,所以它是一个 ELF 文件,因此 vmlinux 文件包含了各种调试信息和各种有用的 section。vmlinux 文件的链接过程由 arch/$(ARCH)/kernel/vmlinux.lds.S 链接脚本决定,可以通过该文件知道 vmlinux 文件的内部布局。 在这里插入图片描述在这里插入图片描述 let’s look at how vmlinux is produced on x86-64: 在这里插入图片描述 内核编译: 在这里插入图片描述 2.3.Image 构建   Image文件是 vmlinux 使用 objcopy 工具转换后得到的二进制文件。由于 vmlinux 不能直接在 arm 上运行,需要丢弃一些与运行无关的 section,所以使用 objcopy 工具完成这个任务。Image 文件相比 vmlinux,除了格式不同之外,vmlinux 的调试信息和 许多注释以及与运行无关的 section 都被移除,所以体积会变小很多。构建过程如下所示:

arch/arm/boot/Makefile : $(obj)/Image: vmlinux FORCE $(call if_changed,objcopy)

  Image 就是通过 vmlinux objcopy 获得,这里 objcopy 对应的命令是 位于 scripts/Makefile.lib 文件中获得,定义如下:

quiet_cmd_objcopy = OBJCOPY $@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@

  测试:在 Image 生成过程中添加打印消息,以此查看整个 object 过程, 添加调试代码如下:

$(obj)/Image: vmlinux FORCE $(warning "OBJCOPYFLAGS: $(OBJCOPYFLAGS)") $(warning "OBJCOPYFLAGS_$(@F): $(OBJCOPYFLAGS_$(@F))") $(call if_changed,objcopy)

编译内核时生成如下信息:

LD vmlinux SORTEX vmlinux SYSMAP System.map arch/arm/boot/Makefile:61: "OBJCOPYFLAGS: -O binary -R .comment -S" arch/arm/boot/Makefile:61: "OBJCOPYFLAGS_Image: " OBJCOPY arch/arm/boot/Image Building modules, stage 2. MODPOST 6 modules Kernel: arch/arm/boot/Image is ready

  vmlinux 使用 objcopy变成 Image 时,使用的参数 是 “-O binary -R .comment -S”,这个参数的意思是:

-O binary 表示生成二进制文件-R .comment 表示移除 .comment section-S 表示移除所有的标志以及重定位信息

2.4.piggy_data构建

  piggy_data是 Image 经过压缩之后得到的压缩文件,具体如下:

//arch/arm/boot/compressed/Makefile: $(obj)/piggy_data: $(obj)/../Image FORCE $(call if_changed,$(compress-y))

  通过上面的内容可知,内核采用的压缩方法由 compress-y 变量决定,其定义如下:

//arch/arm/boot/compressed/Makefile : compress-$(CONFIG_KERNEL_GZIP) = gzip compress-$(CONFIG_KERNEL_LZO) = lzo compress-$(CONFIG_KERNEL_LZMA) = lzma compress-$(CONFIG_KERNEL_XZ) = xzkern compress-$(CONFIG_KERNEL_LZ4) = lz4

  因此内核支持 gzip,lzo,lzma,xzkern, 和 lz4 的压缩方法,具体使用哪种,因此开发者可以在 命令执行处添加调试代码如下:

$(obj)/piggy_data: $(obj)/../Image FORCE $(wraning "compress-y: $(compress-y)") $(call if_changed,$(compress-y))

  Image 常采用了gzip 方法,在 scripts/Makefile.lib 文件中获得具体的 gzip 过程,如下:

quiet_cmd_gzip = GZIP $@ cmd_gzip = cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@

gizp 的参数含义如下:

-n 压缩文件时,不保存原来文件名称以及时间戳-f 强制压缩文件。不理会文件名称或硬链接是否存在以及文件是否为符号链接-9 用 9 调整压缩的速度,-1 或 --fast 表示最快压缩方法 (低压缩比), -9 或者 --best 表示最慢的压缩方法 (高压缩比)

2.5.Bootstrap ELF kernel (vmlinux) 构建过程

  只有纯粹的内核是无法启动的,所以需要在内核的头部加入一些用于 bootstrap loader 功能的代码。 Kbuild 编译系统在 arch/arm/boot/compressed/ 目录下,将 head.S, misc.S, compressed.S 等多个汇编文件汇编成多个可链接的 ELF 目标文件,以此作为内核的 bootstrap loader。在这个步骤,Kbuid 编译系统将这些可链接的目标文件与 piggy.o 文件按链接脚本的内容进行链接,制作出一个带 bootstrap loader 的内核ELF 文件。对于过程要参考 arch/arm/boot/compressed/ 目录下的 Makefile 和 vmlinux.lds.S 文件。 首先通过分析 Makefile 知道链接的文件,具体源码如下:

$(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.o \ $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \ $(bswapsdi2) $(efi-obj-y) FORCE @$(check_for_multiple_zreladdr) $(call if_changed,ld) @$(check_for_bad_syms)

2.5.1.vmlinux.lds 理解

  vmlinux.lds总共有两个,分别是:

arch/arm/kernel/vmlinux.lds(用于生成未压缩的内核image)arch/arm/boot/compressed/vmlinux.lds(用于生成经过压缩的内核image)

这两个lds 文件中的链接地址区别:

arch/arm/kernel/vmlinux.lds:

421 OUTPUT_ARCH(arm) 422 ENTRY(stext) 423 jiffies = jiffies_64; 424 SECTIONS 425 { 426 /* 427 * XXX: The linker does not define how output sections are 428 * assigned to input sections when there are multiple statements 429 * matching the same input section name. There is no documented 430 * order of matching. 431 * 432 * unwind exit sections must be discarded before the rest of the 433 * unwind sections get included. 434 */ 435 /DISCARD/ : { 436 *(.ARM.exidx.exit.text) 437 *(.ARM.extab.exit.text) 438 439 440 441 442 *(.exitcall.exit) 443 *(.alt.smp.init) 444 *(.discard) 445 *(.discard.*) 446 } 447 . = 0xC0000000 + 0x00008000; 448 .head.text : { 449 _text = .; 450 *(.head.text) 451 }

arch/arm/boot/compressed/vmlinux.lds:

10 OUTPUT_ARCH(arm) 11 ENTRY(_start) 12 SECTIONS 13 { 14 /DISCARD/ : { 15 *(.ARM.exidx*) 16 *(.ARM.extab*) 17 /* 18 * Discard any r/w data - this produces a link error if we have any, 19 * which is required for PIC decompression. Local data generates 20 * GOTOFF relocations, which prevents it being relocated independently 21 * of the text/got segments. 22 */ 23 *(.data) 24 } 25 26 . = 0; 27 _text = .;

  压缩内核的链接地址是从0开始的,未压缩的内核链接地址是从0xC0000000+0x00008000开始的。

  首先uboot使用到的内核是压缩过的内核也就是uimage,此时内核链接到的是0地址,入口是_start。而arch/arm/boot/compressed/vmlinux.lds中有如下定义:

.text : { 30 _start = .; 31 *(.start) 32 *(.text) 33 *(.text.*) 34 *(.fixup) 35 *(.gnu.warning) 36 *(.glue_7t) 37 *(.glue_7) 38 }

  所以压缩内核的入口就是链接地址是从0开始的。压缩内核来源于压缩的vmlinux,而压缩的vmlinux是通过head.o, misc.o piggy.o加工而来的。这个压缩的vmlinux连接顺序可以从arch/arm/boot/compressed/Makefile中得到如下信息:

HEAD = head.o OBJS += misc.o decompress.o vmlinux: $(obj)/vmlinux.lds ( o b j ) / (obj)/ (obj)/(HEAD) ( o b j ) / p i g g y . (obj)/piggy. (obj)/piggy.(suffix_y).o

  所以其实压缩内核的入口就是arch/arm/boot/compressed/head.s文件,并且这个文件应该也就是uboot跳转到内核之后的总入口,这部分代码是位置无关,所以链接到0地址也没关系。

  内核的入口的这段代码应该还包含搬运内核到高地址空间的代码,搬运的目的地址应该就是arch/arm/kernel/vmlinux.lds文件中的连接地址0xC0000000+0x00008000,这也应该改是内核最终的运行地址。

2.6 zImage构建

  zImage 是通过带 bootstrap loader 的内核 ELF 文件经过 objcopy 命令之后制作生成 的二进制文件,用于在 arm 上直接运行,其生成过程可以查看

arch/arm/boot/Makefile: $(obj)/zImage: $(obj)/compressed/vmlinux FORCE $(call if_changed,objcopy)

  同原始 vmlinux 转换为 Image 过程一致。制作完 zImage 之后, 可以将 zImage 在 arm 上运行。

  最终在内存SDRAM中的内核镜像是经过压缩的,只是在运行时再将其解压。所以编译时会先使用gzip将镜像文件image进行压缩,再将压缩后的镜像文件和源码中的两个文件(如下所示)一起链接生成压缩后的镜像文件compress/vmlinux,注意,这两个源码文件是解压程序,用于将内存SDRAM中的压缩镜像zImage进行解压,然后再执行kernel 的第一阶段启动代码arch/arm/kernel/head.S。简而言之,在内存中运行内核时,kernel先自身解压,再执行第一阶段启动代码。

arch/arm/boot/compressed/head.Sarch/arm/boot/compressed/misc.c 在这里插入图片描述

3.vmlinuz

  vmlinuz是可引导的、压缩的,能bootable的内核。vmlinux是用来生成vmlinuz的中间步骤。vmlinuz是Linux kernel文件的历史名字,它实际上就是zImage或bzImage。

  vmlinuz 的建立有两种方式:

编译内核时执行"make zImage",zImage适用于小内核的情况,它的存在是为了向后的兼容性。

内核编译时通过命令make bzImage创建。

  zImage、bzImage 中均包含一个微型的 gzip 用于解压缩内核并引导,两者的不同之处在于: zImage 解压缩内核到低端内存 (第一个640K),bzImage 解压缩内核到高端内存 (1M以上)。也就是,它们之间最大的差别是对于内核体积大小的限制。

  由于 zImage 内核需要放在实模式 1MB 的内存之内,所以其体积受到了限制,目前采用的内核格式大多采用的是 bzImage ,这种格式没有 1MB 内存限制。arm 中常用的是 zImage,而 x86 中常用的是 bzImage 。

4.Linux 编译流程图 在这里插入图片描述

参考资料: https://www.jianshu.com/p/d4e9b87c409d https://jin-yang.github.io/post/kernel-compile.html https://biscuitos.github.io/blog/ARM-Kernel-Image/



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3